package logutil

import (
	"context"
	"strings"
	"sync"

	"gitee.com/simonxie979/skymeta/logutil/filewriter"
	"gitee.com/simonxie979/skymeta/logutil/hook"
	"gitee.com/simonxie979/skymeta/logutil/port"

	"github.com/sirupsen/logrus"
)

// 日志对象
type Logger struct {
	ctx    context.Context
	object *logrus.Logger  // 底层日志对象
	writer port.FileWriter // 文件写入对象
	filter sync.Map        // 标签过滤器
}

// 将日志输出到指定文件
func (l *Logger) AddHook_FileLog(logPath, logName string) {
	l.writer = filewriter.NewBakWriter(l.ctx, logPath, logName)
	l.writer.Serve()
	l.object.AddHook(hook.NewHook_FileLog(l.writer))
}

// 将日志输出到指定服务
func (l *Logger) AddHook_LogService(callback port.LogServiceCallBack) {
	l.object.AddHook(hook.NewHook_LogService(callback))
}

// 设置日志等级
func (l *Logger) SetLevel(level string) error {
	lvl, err := logrus.ParseLevel(level)
	if err != nil {
		return err
	}

	l.object.SetLevel(lvl)
	return nil
}

// 添加一个过滤标签
func (l *Logger) AddTagFilter(tag string) {
	l.filter.Store(tag, struct{}{})
}

// 移除一个过滤标签
func (l *Logger) DelTagFilter(tag string) {
	l.filter.Delete(tag)
}

// 重置过滤标签
func (l *Logger) ResetFilter() {
	l.filter.Range(func(key, value any) bool {
		l.filter.Delete(key)
		return true
	})
}

// 等待文件Writer结束
func (l *Logger) Wait() {
	if l.writer == nil {
		return
	}

	for {
		switch l.writer.Done() {
		case true:
			l.writer = nil
			return
		default:
		}
	}
}

// 日志统一处理函数
func (l *Logger) logf(skip int, level logrus.Level, tag, format string, args ...any) {
	if !l.object.IsLevelEnabled(level) {
		return
	}

	if _, exist := l.filter.Load(tag); exist {
		return
	}

	fields := make(logrus.Fields, 3)
	fields[port.TagKey] = tag
	fields[port.FuncKey] = ""
	fields[port.StackTraceKey] = ""
	if level > logrus.ErrorLevel {
		fields[port.FuncKey] = GetCaller(skip + 2)
	} else {
		fields[port.StackTraceKey] = GetCallerStack(skip + 4)
	}

	// 自定义"%v"占位符对应参数的序列化方式
	for i, v := range allPH.FindAllString(format, -1) {
		if vPH.MatchString(v) && len(args) > i {
			format = strings.Replace(format, v, "%s", 1)
			args[i] = Printf(args[i], true)
		}
	}

	l.object.WithFields(fields).Logf(level, format, args...)
}

// Panic log
func (l *Logger) Panicf(tag, format string, args ...any) {
	l.logf(0, logrus.PanicLevel, tag, format, args...)
}

// Fatal log
func (l *Logger) Fatalf(tag, format string, args ...any) {
	l.logf(0, logrus.FatalLevel, tag, format, args...)
}

// Error log
func (l *Logger) Errorf(tag, format string, args ...any) {
	l.logf(0, logrus.ErrorLevel, tag, format, args...)
}

// Warning log
func (l *Logger) Warnf(tag, format string, args ...any) {
	l.logf(0, logrus.WarnLevel, tag, format, args...)
}

// Info log
func (l *Logger) Infof(tag, format string, args ...any) {
	l.logf(0, logrus.InfoLevel, tag, format, args...)
}

// Debug log
func (l *Logger) Debugf(tag, format string, args ...any) {
	l.logf(0, logrus.DebugLevel, tag, format, args...)
}

// Trace log
func (l *Logger) Tracef(tag, format string, args ...any) {
	l.logf(0, logrus.TraceLevel, tag, format, args...)
}

// Panic log, but can specify caller skip
func (l *Logger) Panicf_Skip(skip int, tag, format string, args ...any) {
	l.logf(skip, logrus.PanicLevel, tag, format, args...)
}

// Fatal log, but can specify caller skip
func (l *Logger) Fatalf_Skip(skip int, tag, format string, args ...any) {
	l.logf(skip, logrus.FatalLevel, tag, format, args...)
}

// Error log, but can specify caller skip
func (l *Logger) Errorf_Skip(skip int, tag, format string, args ...any) {
	l.logf(skip, logrus.ErrorLevel, tag, format, args...)
}

// Warning log, but can specify caller skip
func (l *Logger) Warnf_Skip(skip int, tag, format string, args ...any) {
	l.logf(skip, logrus.WarnLevel, tag, format, args...)
}

// Info log, but can specify caller skip
func (l *Logger) Infof_Skip(skip int, tag, format string, args ...any) {
	l.logf(skip, logrus.InfoLevel, tag, format, args...)
}

// Debug log, but can specify caller skip
func (l *Logger) Debugf_Skip(skip int, tag, format string, args ...any) {
	l.logf(skip, logrus.DebugLevel, tag, format, args...)
}

// Trace log, but can specify caller skip
func (l *Logger) Tracef_Skip(skip int, tag, format string, args ...any) {
	l.logf(skip, logrus.TraceLevel, tag, format, args...)
}
